#include "serialport.h"
#include <QsLog/QsLog.h>

#include <QFile>
#include <QDebug>
#include <QtSerialPort/QSerialPortInfo>
#include <QMessageBox>


SerialPort::SerialPort(DataHandler *handler, QObject *parent)
    : QObject(parent), serial(new QSerialPort(this)), dataHandler(handler), m_dataId(0)
{


}


SerialPort::~SerialPort()
{
    //delete serial;
}


bool SerialPort::openPort(const QString &portName)
{
    serial->setPortName(portName);
    serial->setBaudRate(QSerialPort::Baud115200);
    serial->setDataBits(QSerialPort::Data8);
    serial->setParity(QSerialPort::NoParity);
    serial->setStopBits(QSerialPort::OneStop);

    bool fl = false;
    if (serial->open(QIODevice::ReadWrite)) {
        qDebug() << "[太阳辐射计] 串口" << portName << "打开成功.";
        //&SerialPort::readData);
        fl = true;  // 成功打开串口
    } else {
        qWarning() << "[太阳辐射计] 无法打开串口：" << portName << "，错误：" << serial->errorString();
        fl = false; // 打开串口失败
    }

    serial->clear(QSerialPort::AllDirections);

    connect(serial, &QSerialPort::readyRead, this, &SerialPort::readData);
    connect(serial, &QSerialPort::errorOccurred, this, [=](QSerialPort::SerialPortError error){
        QLOG_ERROR()<<"[串口]：串口发生错误："<<error;
    });

    return fl;
}

void SerialPort::refreshSerialPorts(QComboBox *comboBox)
{
    comboBox->clear();
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        comboBox->addItem(info.portName());
    }
    if (comboBox->count() == 0) {
        comboBox->addItem("未找到串口");
        qDebug() << "[太阳辐射计] 未找到串口.";
    } else {
        qDebug() << "[太阳辐射计] 串口已刷新，数量：" << comboBox->count();
    }
}

// void SerialPort::SetZero()
// {
//     QByteArray cmd("MC,m,P,0,0,0x0D");
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = serial->write(cmd);
//     if(-1 == ret){
//         QMessageBox::information(nullptr, "提示", "写入失败");
//     }
// }
// void SerialPort::SetTrace()
// {
//     if(nullptr == serial){
//         QMessageBox::information(nullptr, "提示", "写入失败");
//         return;
//     }

//     QByteArray cmd("MC,m,T,0,0,0x0D");
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = serial->write(cmd);
//     if(-1 == ret){
//         QMessageBox::information(nullptr, "提示", "写入失败");
//     }
// }
// void SerialPort::DeflectRelative()
// {
//     if(nullptr == serial){
//         QMessageBox::information(nullptr, "提示", "串口没有开启");
//         return;
//     }

//     QString azimuth = QString::number(ui->lineEdit_azimuth_relative->text().toDouble(), 'f', 2);
//     QString heading = QString::number(ui->lineEdit_heading_relative->text().toDouble(), 'f', 2);
//     QByteArray cmd = QString("MC,m,O,%1,%2,0x0D").arg(azimuth).arg(heading).toLatin1();
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = serial->write(cmd);
//     if(-1 == ret){
//         QMessageBox::information(nullptr, "提示", "写入失败");
//     }
// }
// void SerialPort::DeflectAbsolute()
// {

//     if(nullptr == m_solarSerial){
//         QMessageBox::information(this, "提示", "串口没有开启");
//         return;
//     }

//     QString azimuth = QString::number(ui->lineEdit_azimuth_absolute->text().toDouble(), 'f', 2);
//     QString heading = QString::number(ui->lineEdit_heading_absolute->text().toDouble(), 'f', 2);
//     QByteArray cmd = QString("MC,m,C,%1,%2,0x0D").arg(azimuth).arg(heading).toLatin1();
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = m_solarSerial->Send(cmd);
//     if(-1 == ret){
//         QMessageBox::information(this, "提示", "写入失败");
//     }
// }
// void SerialPort::SetLongitudeLatitude()
// {
//     if(nullptr == m_solarSerial){
//         QMessageBox::information(this, "提示", "串口没有开启");
//         return;
//     }

//     bool ok = false;
//     double latitude = ui->lineEdit_latitude->text().toDouble(&ok);
//     if(!ok || latitude < 0 || latitude > 90){
//         QMessageBox::critical(this, "设置经纬度失败", "纬度格式错误，纬度范围0~90");
//         return;
//     }
//     double longtitude = ui->lineEdit_longitude->text().toDouble(&ok);
//     if(!ok || longtitude < 0 || longtitude > 180){
//         QMessageBox::critical(this, "设置经纬度失败", "经度格式错误，经度范围0~180");
//         return;
//     }
//     m_latitdude = latitude;
//     m_longitude = longtitude;
//     QString message = QString("设置经纬度成功, 经度：%1， 纬度：%2").arg(QString::number(longtitude), QString::number(latitude));
//     QMessageBox::information(this, "设置经纬度成功", message);

//     //设置每10s设置一次时间和经纬度
//     //取消之前的连接
//     disconnect(&m_setlonLatTimer, 0,0,0);

//     m_setlonLatTimer.setInterval(1000);
//     connect(&m_setlonLatTimer, &QTimer::timeout, this, [=](){
//         /* 发送gps信息 */
//         QString dtStr = QDateTime::currentDateTime().toString("yyyy,MM,dd,HH,mm,ss");
//         QString cmdStr = QString("MC,m,G,%1,%2,%3,0x0D").arg(dtStr, QString::number(m_longitude, 'f', 5), QString::number(m_latitdude, 'f', 5));

//         QByteArray cmd = cmdStr.toLatin1();
//         cmd.append('\r');
//         cmd.append('\n');
//         int ret = m_solarSerial->Send(cmd);
//         if(-1 == ret){
//             QMessageBox::information(this, "提示", "写入失败");
//         }
//     });
//     m_setlonLatTimer.start();
// }
// void SerialPort::SetStop()
// {
//     if(nullptr == m_solarSerial){
//         QMessageBox::information(this, "提示", "串口没有开启");
//         return;
//     }

//     QByteArray cmd("MC,m,Q,0x0D");
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = m_solarSerial->Send(cmd);
//     if(-1 == ret){
//         QMessageBox::information(this, "提示", "写入失败");
//         return;
//     }
// }

// void SerialPort::SearchGPS()
// {
//     if(nullptr == m_solarSerial){
//         QMessageBox::information(this, "提示", "串口没有开启");
//         return;
//     }

//     QByteArray cmd("MC,m,G,0x0D");
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = m_solarSerial->Send(cmd);
//     if(-1 == ret){
//         QMessageBox::information(this, "提示", "写入失败");
//     }
// }
// void SerialPort::SearchStatus()
// {
//     if(nullptr == m_solarSerial){
//         QMessageBox::information(this, "提示", "串口没有开启");
//         return;
//     }

//     QByteArray cmd("MC,m,S,0x0D");
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = m_solarSerial->Send(cmd);
//     if(-1 == ret){
//         QMessageBox::information(this, "提示", "写入失败");
//     }
// }
// void SerialPort::SearchAngle()
// {
//     if(nullptr == m_solarSerial){
//         QMessageBox::information(this, "提示", "串口没有开启");
//         return;
//     }

//     QByteArray cmd("MC,m,A,0x0D");
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = m_solarSerial->Send(cmd);
//     if(ret <= 0){
//         QMessageBox::information(this, "提示", "写入失败");
//     }
// }
// void SerialPort::SearchSolarAngle()
// {
//     if(nullptr == m_solarSerial){
//         QMessageBox::information(this, "提示", "串口没有开启");
//         return;
//     }

//     QByteArray cmd("MC,m,N,0x0D");
//     cmd.append('\r');
//     cmd.append('\n');
//     int ret = m_solarSerial->Send(cmd);
//     if(ret <= 0){
//         QMessageBox::information(this, "提示", "写入失败");
//     }
// }


void SerialPort::closePort()
{
    serial->close();
}

bool SerialPort::isOpen() const
{
    return serial->isOpen();
}

//滤光片测试指令未加校验位 55 AA 01 10 37 33 34 30 6E 6D 2C 33 38 30 6E 6D 2C 34 34 30 6E 6D 2C 35 30 30 6E 6D 2C 36 37 36 6E 6D 2C 38 37 30 6E 6D 2C 39 33 37 6E 6D 2C 31 30 32 30 6E 6D 2C 31 36 35 30 6E 6D 00
//数据测试未加校验位 55 AA 01 15 25 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 00
void SerialPort::readData()
{
    QByteArray tempBuffer = serial->readAll();
    buffer.append(tempBuffer);  // 将接收到的数据添加到缓冲区
    QLOG_INFO()<< "[太阳辐射计收到] 接收到数据：" << tempBuffer.toHex(' ').toUpper();
    qDebug()<<"[太阳辐射计收到] 接收到数据：" << tempBuffer.toHex();
    // 处理缓冲区中的数据
    while (buffer.size() >= 7)  // 每帧数据最少6字节
    {
        // 从缓冲区的第一个字节开始判断是否为帧头
        if (static_cast<quint8>(buffer[0]) != 0x55 || static_cast<quint8>(buffer[1]) != 0xAA  ) {
            // 如果不是帧头，丢弃缓冲区的第一个字节
            buffer.remove(0, 1);
            continue;
        }

        // 至少有7个字节才能继续解析
        if (buffer.size() < 7) {
            break;  // 不足以构成完整的帧，等待更多数据
        }

        // 检查完帧头后，计算数据长度
        quint8 dataLength = static_cast<quint8>(buffer[4]);
        if (dataLength == 0xFF) {
            if (buffer.size() < 9) {
                break;  // 不足以读取两个字节的长度，等待更多数据
            }
            dataLength = (static_cast<quint8>(buffer[5]) << 8) | static_cast<quint8>(buffer[6]);
        }

        int frameLength = 6 + dataLength + 1; // 计算完整数据帧的长度，包括包头、控制类型、命令、数据、校验字

        QByteArray frame = buffer.left(frameLength);  // 取出完整的数据帧
        // 计算校验位
        QByteArray dataForChecksum = frame.mid(2, frame.length() - 3); // 校验位不包括在内

        quint8 calculatedChecksum = dataHandler->calculateChecksum(dataForChecksum);

        // 检查校验位
        if (calculatedChecksum == static_cast<quint8>(frame[frame.size() - 1])) {
            // 如果校验位正确，发出信号处理数据
            QLOG_INFO()<< "[太阳辐射计收到] 接收到有效数据：" << tempBuffer.toHex(' ').toUpper();
            emit dataHandler->dataProcessedToStruct(frame);
            // 移除已处理的数据
            //buffer.remove(0, frameLength);
            buffer.remove(0, buffer.length());
        } else {
            return;
        }
    }
}


void SerialPort::sendCommand(quint8 command, quint8 dataLength, const QByteArray &data)
{
    if (!serial->isOpen()) {
        QLOG_ERROR()<< "[太阳辐射计] 串口未打开，无法发送数据";
        return;  // 退出函数
    }
    QByteArray packet;
    packet.append(static_cast<char>(0x55));  // 包头1
    packet.append(static_cast<char>(0xAA));  // 包头2
    packet.append(static_cast<char>(0x01));  // 控制类型
    packet.append(static_cast<char>(command));  // 命令

    // 如果数据长度大于254字节，采用特殊处理方式
    if (dataLength > 254) {
        packet.append(static_cast<char>(0xFF));
        packet.append(static_cast<char>(dataLength >> 8));  // 数据长度高字节
        packet.append(static_cast<char>(dataLength & 0xFF));  // 数据长度低字节
    } else {
        packet.append(static_cast<char>(dataLength));  // 数据长度
    }

    packet.append(data);  // 数据内容
   // QByteArray dataForChecksum = packet.mid(2, packet.size() - 2);
    // 计算校验字，从控制类型到数据的异或值
    quint8 calculatedChecksum =dataHandler->calculateChecksum(packet.mid(2, packet.size() - 2));

    packet.append(static_cast<char>(calculatedChecksum));  // 校验字
    packet.append('\r');
    packet.append('\n');


    qint64 sendSize = serial->write(packet);  // 发送数据包
    qDebug()<<"指令： "<<packet.toHex().toUpper();
    serial->flush();
    if(0 == sendSize || -1 == sendSize){
        QLOG_ERROR()<<"[串口]串口发生错误:"<<serial->error();
    }
}

/**
 * @brief SerialPort::sendHandshakeCommand 发送握手信号命令。
 */
void SerialPort::sendHandshakeCommand()
{
    QLOG_INFO()<<"[太阳辐射计]发送握手指令";
    sendCommand(0x09, 0x01, QByteArray(1, 0x01));  // 发送命令，示例数据
}

/**
 * @brief SerialPort::sendDeviceVersionQuery 发送查询设备版本号命令。
 */
void SerialPort::sendDeviceVersionQuery()
{
    QLOG_INFO()<<"[太阳辐射计]发送查询设备版本号指令";
    sendCommand(0x0F, 0x00, QByteArray());  // 发送命令，无数据
}

/**
 * @brief SerialPort::sendFilterTypesQuery 发送查询滤光片类型命令。
 */
void SerialPort::sendFilterTypesQuery()
{
    QLOG_INFO()<<"[太阳辐射计]发送查询滤光片类型指令";
    sendCommand(0x10, 0x00, QByteArray());  // 发送命令，无数据
}

/**
 * @brief SerialPort::startAutoMeasurement 发送启动自动测量数据命令。
 */
void SerialPort::startAutoMeasurement()
{
    m_dataId++;
    qDebug()<<"m_dataId : "<<QByteArray(1, m_dataId).toHex();
    QLOG_INFO()<<"[太阳辐射计]发送测量数据指令，id:"<<QByteArray(1, m_dataId).toHex();
    sendCommand(0x14, 0x01, QByteArray(1, m_dataId));  // 发送命令，示例数据
    //sendCommand(0x14, 0x01, QByteArray(1, 0x01));  // 发送命令，示例数据
}

/**
 * @brief SerialPort::readAutoMeasurementData 发送读取自动测量数据命令。
 */
void SerialPort::readAutoMeasurementData()
{
    QLOG_INFO()<<"[太阳辐射计]发送读取测量数据指令";
    sendCommand(0x15, 0x00, QByteArray());  // 发送命令，无数据
}

